package end.handler;

import end.bean.LogInfo;
import end.bean.shiro.Manager;
import end.handler.aop.log.SystemControllerLog;
import end.handler.aop.log.SystemSERLog;
import end.ser.*;
import end.constant.Const;
import end.util.DateUtils;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 
 * SystemLogAspect.java 2016-7-8
 * 
 * Copyright zhaocj Inc. All rights reserved. Love Me Like Love Justin Bieber
 */

@Aspect
@Component
public class SystemLogAspect {

	private static final Logger logger = LoggerFactory
			.getLogger(SystemLogAspect.class);

	@Resource
	private LogInfoSER<LogInfo> logInfoSER;

	public LogInfoSER<LogInfo> getLogInfoSER() {
		return logInfoSER;
	}

	public void setLogInfoSER(LogInfoSER<LogInfo> logInfoSER) {
		this.logInfoSER = logInfoSER;
	}

	// 方法层切入点
	@Pointcut("@annotation(end.handler.aop.log.SystemSERLog)")
	public void serviceAspect() {
	}

	// 接收层切入点
	@Pointcut("@annotation(end.handler.aop.log.SystemControllerLog)")
	public void controllerAspect() {
	}

	/**
	 * 前置通知 用于拦截Controller层记录用户的操作
	 */
	@Before("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) {

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
		HttpSession session = request.getSession();
		Manager currentManager = (Manager) session
				.getAttribute(Const.SESSION_USER);
		String ip = request.getRemoteAddr();
		try {
			SystemControllerLog controllerLogAnno = getControllerLogAnno(joinPoint);
			if (logger.isDebugEnabled()) {
				System.out.println("=====前置通知开始=====");
				System.out.println("请求方法:"
						+ (joinPoint.getTarget().getClass().getName() + "."
								+ joinPoint.getSignature().getName() + "()"));
				System.out.println("操作方法介绍:" + controllerLogAnno.description());
				System.out.println("操作类型         :"
						+ controllerLogAnno.logType());
				System.out
						.println("操作方法类型:" + controllerLogAnno.logOperState());
				System.out.println("操作的内容     :"
						+ getOpertionLOGByAnno(controllerLogAnno.clazz(),
								joinPoint));
				System.out.println("请求人:" + currentManager.getName());
				System.out.println("请求IP:" + ip);
			}
			LogInfo logInfo = new LogInfo();
			logInfo.setCreateDate(DateUtils.getDateTime());
			logInfo.setCode(currentManager.getName());
			// 设置了调用的方法的注解描述
			logInfo.setOperation(controllerLogAnno.description());
			logInfo.setContent(getOpertionLOGByAnno(controllerLogAnno.clazz(),
					joinPoint));
			logInfo.setOperType(controllerLogAnno.logType());
			logInfo.setSubOperType(controllerLogAnno.logOperState());
			logInfo.setRequestIP(ip);
			logInfoSER.insert(logInfo);
			if (logger.isDebugEnabled()) {
				System.out.println("=====前置通知结束=====");
			}
		} catch (Exception e) {
			// 记录本地异常日志
			logger.error("==前置通知异常==");
			logger.error("异常信息:{}", e.getMessage());
		}
	}

	/**
	 * 异常通知 用于拦截service层记录异常日志
	 */
	@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
		HttpSession session = request.getSession();
		// 读取session中的用户
		String nickName = (String) session.getAttribute(Const.SESSION_USER);
		// 获取请求ip
		String ip = request.getRemoteAddr();
		try {
			/* ========控制台输出========= */
			System.out.println("=====异常通知开始=====");
			System.out.println("异常代码:" + e.getClass().getName());
			System.out.println("异常信息:" + e.getMessage());
			System.out.println("异常方法:"
					+ (joinPoint.getTarget().getClass().getName() + "."
							+ joinPoint.getSignature().getName() + "()"));
			SystemControllerLog controllerLogAnno = getControllerLogAnno(joinPoint);
			System.out.println("操作方法介绍:" + controllerLogAnno.description());
			System.out.println("操作类型:" + controllerLogAnno.logType());
			System.out.println("操作方法类型:：" + controllerLogAnno.logOperState());
			System.out.println("请求人:" + nickName);
			System.out.println("请求IP:" + ip);
			/* ==========数据库日志========= */
			LogInfo logInfo = new LogInfo();
			logInfo.setCreateDate(DateUtils.getDateTime());
			logInfo.setCode(nickName);
			// logInfoSER.insert(logInfo);
			// System.out.println("=====异常通知结束=====");
		} catch (Exception ex) {
			// 记录本地异常日志
			logger.error("==异常通知异常==");
			logger.error("异常信息:{}", ex.getMessage());
		}
	}

	/**
	 * 获取注解中对方法的描述信息 用于service层注解
	 */
	public static SystemSERLog getServiceLogAnno(JoinPoint joinPoint)
			throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class<?> targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class<?>[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					return method.getAnnotation(SystemSERLog.class);
				}
			}
		}
		return null;
	}

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * 
	 */
	public static SystemControllerLog getControllerLogAnno(JoinPoint joinPoint)
			throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class<?> targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class<?>[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					return method.getAnnotation(SystemControllerLog.class);
				}
			}
		}
		return null;
	}

	/**
	 * 
	 * 获取拦截参数中的注解对应的对象
	 * 
	 */
	public static String getOpertionLOGByAnno(Class<?> clazz,
			JoinPoint joinPoint) throws Exception {
		Object[] arguments = joinPoint.getArgs();
		String opertionLOG = "";
		for (Object object : arguments) {
			if (object.getClass().equals(clazz)) {
				Class<?> superclass = clazz.getSuperclass();
				Field field = superclass.getDeclaredField("operationLOG");
				field.setAccessible(true);
				return field.get(object).toString();
				/*
				 * Method method = clazz.getMethod("getOperationLOG",
				 * Null.class); opertionLOG
				 * =(String)method.invoke(object,Null.class);
				 */
			}
		}
		return opertionLOG;
	}
}
